/* один раз, при первой загрузке страницы */
DOMReady( 
	function(){
		window.onpopstate = function( e ) {
			/* получаем нормальный объект Location
			* 
			* заметьте, это единственная разница при работе с данной библиотекой,
			* так как объект document.location нельзя переопределить, поэтому
			* библиотека history возвращает сформированный "location" объект внутри
			* объекта window.history, поэтому получаем его из "history.location".
			* Для браузеров поддерживающих "history.pushState" получаем
			* сформированный объект "location" с обычного "document.location".
			*/
			var returnLocation = history.location || document.location;
			// тут можете вызвать подгруздку данных и т.п.
			ajaxRequest( returnLocation.href, {
				dataType:'json'
				//, success: function( data, textStatus, xhr ) { outContent( data ); }
			});
		}
	
		anchorsUpdate();
		scriptStack.call();//updateStackRun();
	}
);

/* обновление ссылок */
function anchorsUpdate(){
	// ищем все ссылки
	var anchors = document.getElementsByTagName( 'a' );
	// вешаем события на все ссылки в нашем документе
	for( var i = 0; i < anchors.length; i++ ) {
		if( anchors[ i ].getAttribute('data-noajax')==null )
		anchors[ i ].onclick = function(){
			// заносим ссылку в историю
			history.pushState( null, null, this.href );
			// тут можете вызвать подгруздку данных и т.п.
			ajaxRequest( this.href, { dataType:'json'});
			// не даем выполнить действие по умолчанию
			return false;
		};
	}
}

/* стек перезапускаемых скриптов при перезагрузке элементов DOM */
if(!scriptStack){
	var scriptStack = {
		scripts: {}
		
		,add: function( scriptJS ){ 
			var key=0, found=false;
			for(key in this.scripts ) if( this.scripts[key] == scriptJS ) found=true;/* найден */
			if( !found ) this.scripts[ +key+1 ] = scriptJS;
		}
		,call: function(){
			for(var key in this.scripts )
				( window.execScript || function( data ) {
					window[ "eval" ].call( window, data ); //console.log( data );
				} )( this.scripts[ key ] );
		}
	};
}	
	
/* обновление модульных скриптов */
function scriptsUpdate(){
	var scripts = document.getElementsByTagName( 'script' );
	for(var j=0; j<scripts.length; j++){
		if( scripts[ j ].getAttribute('src')==undefined ) continue;

		tag = document.createElement('script');
		tag.src = scripts[ j ].getAttribute('src');
		tag.type = "text/javascript";
		
		scripts[ j ].parentNode.appendChild( tag );
		scripts[ j ].parentNode.removeChild( scripts[ j ] );
	}
}	

/* последние данные ответа для отладки */
if(!lastResponseData){
	var lastResponseData='empty';
}

/* AJAX REQUEST */
function ajaxRequest( url, options ){

	if ( typeof url === "object" ) {
		options = url;
		url = undefined;
	}

	options = options || {};
	
	if ( typeof options === "function" ) {
		options = { success: options };
	}

	options.type = options.type || 'get';
	options.dataType = options.dataType || 'html';
	options.async = options.async === undefined ? true : options.async;

	var data = null;

	if ( options.data ) {
		if ( typeof options.data == "object" ) {
			for( var i in options.data ) {
				data = ( ( data == null ) ? "" : data + "&" ) + ( encodeURIComponent(i) + "=" + encodeURIComponent(options.data[ i ]) );
			}
		} else {
			data = options.data;
		}
	}

	url = url || options.url;

	url = ( url + "" ).replace( /#.*$/, "" );

	var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"),
		perhapsJSON, textStatus, error = false;

	xhr.onreadystatechange = function() {

		if ( xhr.readyState == 4 ) {

			if ( xhr.status >= 200 && xhr.status < 300 || xhr.status === 304 || xhr.status === 404) {

				perhapsJSON = xhr.responseText;
				textStatus = xhr.statusText || "";

				try {
					if ( options.dataType == 'json' ) {
						if ( window.JSON && JSON.parse ) {
							perhapsJSON = JSON.parse( perhapsJSON );
						} else {
							perhapsJSON = (new Function( "return " + perhapsJSON ))();
						}
					}
				} catch( _ ) {

					error = true;

					if ( ajaxEvents.error ) {
						for(var i in ajaxEvents.error ) ajaxEvents.error[i].call( xhr, xhr, "JSON Error" );
					}
				}
				
				lastResponseData = perhapsJSON;
				updateOutData( perhapsJSON );

				if ( !error ) {
					if ( ajaxEvents.success ) {
						for(var i in ajaxEvents.success ) ajaxEvents.success[i].call( xhr, perhapsJSON, textStatus, xhr );
					}
				}

				if ( ajaxEvents.complete ) {
					for(var i in ajaxEvents.complete ) ajaxEvents.complete[i].call( xhr, xhr, textStatus );
				}

			} else {
				if ( ajaxEvents.error ) {
					for(var i in ajaxEvents.error ) ajaxEvents.error[i].call( xhr, xhr, xhr.statusText );
				}
			}

			xhr = null;
		}
	}

	xhr.open( options.type, url, options.async );

	try {
		xhr.setRequestHeader( "X-Requested-With", "XMLHttpRequest" );
		if ( options.type == "post" ) {
			xhr.setRequestHeader( "Content-Type", "application/x-www-form-urlencoded" );
		}
	} catch( _e_ ) {}

	xhr.send( data );

	return xhr;
}

/* вывод и обновление визуальных данных */
function updateOutData( data ) {
	if(!data || !data.out_data) return;
	var updated = {};
	
	for( var id in data.out_data ){
		var elem = document.getElementById( id );

		if( !elem || elem.innerHTML==undefined ) debug( id+' isn\'t a HTMLElement or not a single', 'out_data');
		else {
			appendHTML( elem, data.out_data[id] );
			updated[id]=data.out_data[id];/* для отладки */
		}
	}

	anchorsUpdate();
	scriptsUpdate();
	scriptStack.call();//updateStackRun();
}

function appendHTML( elem, html, replace ) {

	replace = replace || true; //режим "замена"
	var i, child, fragment, tempDiv, script = [];

	if ( html === undefined ) {
		return elem.innerHTML;
	}

	html = html.replace( /<script(?:[^>]*src="([^"]+)")?[^>]*>([\s\S]*?)<\/script>/ig, function( all, src, text ) {
		script[ script.length ] = src ? { src: src } : { text: text };
		return "";
	});

	tempDiv = document.createElement('DIV');

	tempDiv.innerHTML = html;

	if ( tempDiv.childNodes.length == 1 ) {
		fragment = ( tempDiv.removeChild( tempDiv.firstChild ) );
	} else {
		fragment = document.createDocumentFragment();
		while( tempDiv.firstChild ) {
			fragment.appendChild( tempDiv.firstChild );
		}
	}

	if ( replace ) {
		while( child = elem.firstChild ) {
			elem.removeChild( child );
		}
	}

	try {
		elem.appendChild( fragment );
	} catch ( _ ) {
		try {
			elem.innerHTML += html;
		} catch ( _ ) {
			try {
				elem.innerText += html;
			} catch ( _ ) {
				elem.text += html;
			}
		}
	}

	for( i = 0; i < script.length; i++ ) {
		if ( script[ i ].src ) {
			var scripts = document.getElementsByTagName( 'script' );
			for(var j=0; j<scripts.length; j++)
				if( scripts[ j ].getAttribute('src') == script[ i ].src ) {
					scripts[ j ].parentNode.removeChild( scripts[ j ] );
				}

			var head = document.getElementsByTagName( 'head' )[ 0 ],
				tag = document.createElement('script');
			tag.src = script[ i ].src;
			tag.type = "text/javascript";
			head.appendChild( tag );			

		} else {
			scriptStack.add( script[ i ].text ); /* кладем в стек */
			/*
			( window.execScript || function( data ) {
				window[ "eval" ].call( window, data ); //console.log( data );
			} )( script[ i ].text );*/
		}
	}

}

function getRequestBody( form ) { 
	var aParams = new Array();
	for(var i = 0; i < form.elements.length; i++) {
		var sParam = encodeURIComponent( form.elements[i].name );
		sParam += "=";
		sParam += encodeURIComponent( form.elements[i].value );
		aParams.push(sParam);
	}
	return aParams.join("&");
}


/* стек событий при отработке ajax, нужен ли ??? */
if(!ajaxEvents){
	var ajaxEvents = {
		success: {},
		complete: {},
		error: {},
		
		add: function( result, key, func ){ /* ajaxEvents.add( 'success','login_form_', function(){ ... } ) */
			if(key in this[result] ) return;
			this[result][key] = func;
		}
	};
}

/*
ajaxRequest({
	url: "/tratata/sfsdfsdfsd.sdf",
	data: {"aasa":"SDf"},
	dataType: "json",
	type: "post",
	success: function( data, textStatus, xhr ) {
		
	},
	complete: function( xhr, textStatus ) {
		
	},
	error: function( xhr, textStatus ) {
		alert( xhr.responseText );
		alert(xhr.status);
	}
});

ajaxRequest('/tratata/', {
	error: function( xhr, textStatus ) {
		alert(xhr.status);
	}
});
*/


/* стек обновлений свойств и методов при перезагрузке элементов DOM 
if(!updateStack){
	var updateStack = {
		functions: {},
		
		add: function( key, func ){ // updateStack.add( 'login_form_submit', function(){ ... } ) 
			if(key in this.functions ) return; this.functions[key] = func;
		},
		edit: function( key, func ){ // редактирование функции в стеке 
			if(key in this.functions ) this.functions[key] = func;
		},
		del: function( key, func ){ // удаление функции из стека 
			if(key in this.functions ) delete this.functions[key];
		}		
	};
}
*/
/* запуск обновлений элементов DOM 
function updateStackRun(){
	for(var i in updateStack.functions) 
		if( typeof(updateStack.functions[i])=='function' ) updateStack.functions[i].call( this );
}
*/